﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace GMDViewer
{

	class GMDModel : IDisposable
	{
		public class GMDSubSet
		{
			public Texture2D m_Texture = null;
			public int iTextureHasAlphaFlag = 0;
			public int PrimitiveCount = 0;

			public string strTextureName;		//子集引用纹理名字串
			public int iVStart;					//段落在顶点数组中的起点
			public int iVLength;				//段落顶点长度
			public int iIStart;					//段落在索引数组中的起点
			public int iILength;				//段落索引长度
		}

		public GMDModel(string fileName, GraphicsDevice device)
		{
			BinaryReader br = null;
			FileStream fs = null;

			string TextureSearchPath0 = Path.GetDirectoryName(fileName);
			string TextureSearchPath1 = Path.GetDirectoryName(TextureSearchPath0) + @"\TEX";

			if (fileName.StartsWith("http"))
			{
				HttpWebRequest httpReq = WebRequest.Create(fileName) as HttpWebRequest;
				httpReq.Method = WebRequestMethods.Http.Get;
				httpReq.Credentials = new NetworkCredential();
				HttpWebResponse httpResp = httpReq.GetResponse() as HttpWebResponse;
				br = new BinaryReader(httpResp.GetResponseStream(), Encoding.GetEncoding("GB18030"));
			}
			else
			{
				fs = new FileStream(fileName, FileMode.Open);
				br = new BinaryReader(fs, Encoding.GetEncoding("GB18030"));
			}

			//file header
			char[] header = br.ReadChars(c_GMDFileHeaderTag.Length);
			if (c_GMDFileHeaderTag != new string(header))
			{
				throw new Exception("Not a GMD file.");
			}

			//boundingbox flag
			byte hasBoundingBoxFlag = br.ReadByte();
			bool bHasBB = (hasBoundingBoxFlag == 0x80);

			//gap
			br.ReadBytes(2);

			//alpha options
			char alphaOptions = br.ReadChar();

			//read bounding box info
			if (bHasBB)
			{
				m_BoundingBox = new BoundingBox(
					new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
					new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()));
			}

			m_numSubSet = br.ReadInt32();
			m_numVertices = br.ReadInt32();
			m_numIndices = br.ReadInt32();

			m_SubSets = new GMDSubSet[m_numSubSet];

			for (int i = 0; i < m_numSubSet; i++)
			{
				m_SubSets[i] = new GMDSubSet();
				m_SubSets[i].strTextureName = br.ReadString();

				TextureCreationParameters tcparam = new TextureCreationParameters(
					0, 0, 0, 0, SurfaceFormat.Unknown, TextureUsage.None,
					Color.White, FilterOptions.Dither | FilterOptions.Triangle, FilterOptions.Box);

				try
				{
					string validTexfn = "";
					string texfn = TextureSearchPath0 + @"\" + m_SubSets[i].strTextureName;
					if (File.Exists(texfn))
					{
						validTexfn = texfn;
					}
					else
					{
						texfn = TextureSearchPath1 + @"\" + m_SubSets[i].strTextureName;
						if (File.Exists(texfn))
						{
							validTexfn = texfn;
						}
					}
					if (validTexfn != "")
					{
						m_SubSets[i].m_Texture = Texture2D.FromFile(device, texfn, tcparam);
					}
				}
				catch
				{
					m_SubSets[i].m_Texture = null;
				}

				m_SubSets[i].iTextureHasAlphaFlag = br.ReadInt32();
				int segmentCount = br.ReadInt32();
				if (segmentCount != 1)
				{
					throw new Exception("GMD File content error!");
				}
				m_SubSets[i].iVStart = br.ReadInt32();
				m_SubSets[i].iVLength = br.ReadInt32();
				m_SubSets[i].iIStart = br.ReadInt32();
				m_SubSets[i].iILength = br.ReadInt32();
			}

			m_PositionBuffer = new Vector3[m_numVertices];
			m_NormalBuffer = new Vector3[m_numVertices];
			m_TexCoordBuffer = new Vector2[m_numVertices];
			m_IndicesBuffer = new short[m_numIndices];

			float minx = float.MaxValue, miny = float.MaxValue, minz = float.MaxValue;
			float maxx = float.MinValue, maxy = float.MinValue, maxz = float.MinValue;

			for (int i = 0; i < m_numVertices; i++)
			{
				m_PositionBuffer[i].X = br.ReadSingle();
				m_PositionBuffer[i].Y = br.ReadSingle();
				m_PositionBuffer[i].Z = br.ReadSingle();
				m_NormalBuffer[i].X = br.ReadSingle();
				m_NormalBuffer[i].Y = br.ReadSingle();
				m_NormalBuffer[i].Z = br.ReadSingle();
				int color = br.ReadInt32();
				m_TexCoordBuffer[i].X = br.ReadSingle();
				m_TexCoordBuffer[i].Y = br.ReadSingle();

				if(!bHasBB)
				{
					minx = Math.Min(m_PositionBuffer[i].X, minx);
					miny = Math.Min(m_PositionBuffer[i].Y, miny);
					minz = Math.Min(m_PositionBuffer[i].Z, minz);
					maxx = Math.Max(m_PositionBuffer[i].X, maxx);
					maxy = Math.Max(m_PositionBuffer[i].Y, maxy);
					maxz = Math.Max(m_PositionBuffer[i].Z, maxz);
				}
			}

			if (!bHasBB)
			{
				m_BoundingBox.Min = new Vector3(minx, miny, minz);
				m_BoundingBox.Max = new Vector3(maxx, maxy, maxz);
			}

			for (int i = 0; i < m_numIndices; i++)			//读索引数据
			{
				m_IndicesBuffer[i] = br.ReadInt16();
			}

			br.Close();
			if (fs != null)
			{
				fs.Dispose();
			}

			m_VBPosition = new VertexBuffer(device, typeof(Vector3), m_numVertices, BufferUsage.None);
			m_VBNormal = new VertexBuffer(device, typeof(Vector3), m_numVertices, BufferUsage.None);
			m_VBTexCoord = new VertexBuffer(device, typeof(Vector2), m_numVertices, BufferUsage.None);
			m_IB = new IndexBuffer(device, typeof(short), m_numIndices, BufferUsage.None);

			m_VBPosition.SetData<Vector3>(m_PositionBuffer);
			m_VBNormal.SetData<Vector3>(m_NormalBuffer);
			m_VBTexCoord.SetData<Vector2>(m_TexCoordBuffer);
			m_IB.SetData<short>(m_IndicesBuffer);
		}

		public void Dispose()
		{
			m_VBPosition.Dispose();
			m_VBNormal.Dispose();
			m_VBTexCoord.Dispose();
			m_IB.Dispose();

			foreach (GMDSubSet subset in m_SubSets)
			{
				if (subset.m_Texture != null)
				{
					subset.m_Texture.Dispose();
				}
			}
			m_SubSets = null;
			m_PositionBuffer = null;
			m_NormalBuffer = null;
			m_TexCoordBuffer = null;
			m_IndicesBuffer = null;
			GC.SuppressFinalize(this);
		}

		public VertexBuffer m_VBPosition = null;
		public VertexBuffer m_VBNormal = null;
		public VertexBuffer m_VBTexCoord = null;
		public IndexBuffer m_IB = null;

		public BoundingBox m_BoundingBox;
		public Int32 m_numSubSet;
		public Int32 m_numVertices;
		public Int32 m_numIndices;
		public GMDSubSet[] m_SubSets;

		public Vector3[] m_PositionBuffer;
		public Vector3[] m_NormalBuffer;
		public Vector2[] m_TexCoordBuffer;
		public short[] m_IndicesBuffer;

		const string c_GMDFileHeaderTag = "00GeoVEModel";
	}
}